#ifndef IOSTREAM_OPERATION_H
#define IOSTREAM_OPERATION_H

#pragma once

#include <string.h>            // for memcpy
#include <fstream>             // for ofstream, ifstream, istream, basic_ist...
#include <iomanip>             // for operator<<, setprecision
#include <string>              // for string, getline
#include <vector>              // for vector
#include <sstream>             // for file
#include <memory>

#define E_NO_ERROR 0
#define E_FILE_NOT_EXIST -1

int SplitString(const std::string& input, const std::string& isAnyOf, std::vector<double>& result) 
{
    // 清空 result，以便存储新的数据
    result.clear();

    std::istringstream ss(input);
    std::string token;
    
    while (std::getline(ss, token, isAnyOf[0])) 
    {
        try 
        {
            double value = std::stod(token); // 尝试将子字符串解析为 double
            result.push_back(value); // 将解析后的值添加到结果向量
        } catch (const std::invalid_argument&) 
        {
            // 如果无法解析为 double，跳过并继续下一个子字符串
        }
    }

    // 返回成功分割的值的数量
    return result.size();
}

int StrToDouble(const char* in, double& value) {
    // 保存当前 locale 设置
    std::locale originalLocale = std::locale::global(std::locale::classic());

    std::istringstream ss(in);
    ss.imbue(std::locale::classic()); // 使用 C 语言环境（C locale）进行解析

    if (!(ss >> value)) {
        // 解析失败，抛出异常
        std::locale::global(originalLocale); // 恢复原始 locale 设置
        throw std::runtime_error("Failed to convert the string to a double.");
    }

    // 恢复原始 locale 设置
    std::locale::global(originalLocale);

    return 0; // 成功转换为 double
}

/**
 * @brief 重载运算符operator <<, 将
 * @param out: 输出字符流
 * @param data: 输出到标准输出字符流的数据
 * @return: 输出字符流
 */
inline std::ostream& operator<<(std::ostream &out, const std::vector<double>& data)
{
    unsigned int i = 0;
    for (auto &&it : data)
    {
        out << it;
        if (i++ != (data.size() - 1))
            out << ", ";
    }

    return out;
}

/**
 * @brief 将结构数据写入到文件中
 * @param data: 需要存储的数据
 * @param name: 存储的文件名
 * @param spliter: 数据的分隔符
 * @param append: 是否在文件末尾追加数据
 */
template <typename T>
int WriteToFile2D(const T& data, const std::string& file_name, const std::string& spliter = ",", bool append = false)
{
    std::ofstream file;
    if(append)
        file.open(file_name, std::ios_base::app);
    else
        file.open(file_name);
    file.setf(std::ios::fixed);

    for (int i = 0; i < data.size(); i++)
    {
        for (int j = 0; j < data[i].size() - 1; j++)
            file << data[i][j] << spliter;
        file << data[i][data[i].size() - 1] << std::endl;
    }
    file.close();

    return E_NO_ERROR;
}

/**
 * @brief 将一维数据写入到列文件中
 * @param data: 需要存储的数据
 * @param name: 存储的文件名
 * @param append: 是否在文件末尾追加数据
 */
template <typename T>
int WriteToFile1D(const T& data, const std::string& file_name, bool append = false)
{
    std::ofstream file;
    if(append)
        file.open(file_name, std::ios_base::app);
    else
        file.open(file_name, std::ios::out | std::ios::trunc);

    for(unsigned i = 0; i < data.size(); i++)
        file << data[i] << std::endl;

    file.close();

    return E_NO_ERROR;
}

/**
 * @brief 将矩阵数据写入到文件中
 * @param data: 需要存储的数据
 * @param name: 存储的文件名
 * @param spliter: 数据的分隔符
 * @param precision: 数据的存储精度
 */
template <typename T>
void WriteMatrixToFile(const T& data, const std::string& name, const std::string& spliter = ",", const int& precision = 20)
{
    std::ofstream file(name);
    for (int i = 0; i < data.rows(); i++)
    {
        for (int j = 0; j < data.cols(); j++)
            file << std::setprecision(precision) << data(i, j) << spliter;
        file << std::endl;
    }
    file.close();
}

/**
 * @brief 从文件读取结构数据, 类型T不支持std::array等结构
 * @param data: 被存储的数据
 * @param name: 文件名
 * @param spliter: 数据分隔符
 * @return if < 0, 表示读取文件失败
 */
template <typename T>
int ReadFromFile2D(const std::string& file_name, const std::string spliter, std::vector<T>& data)
{
    int ret = E_NO_ERROR;
    std::ifstream file;
    file.open(file_name);
    if (!file)
        return E_FILE_NOT_EXIST;  // open file fail!

    data.clear();
    std::string s;
    while (getline(file, s))
    {
        std::vector<double> result;   
        if((ret = SplitString(s, spliter, result)) < E_NO_ERROR)
            return ret;

        int N = result.size();
        T                   value(N);
        memcpy(value.data(), result.data(), sizeof(double) * value.size());
        data.push_back(value);
    }

    file.close();
    return ret;
}

template <typename T>
int ReadFromFile1D(const std::string& file_name, T& data)
{
    data.clear();
    std::ifstream file;
    file.open(file_name);
    if (!file)
        return E_FILE_NOT_EXIST;  // open file fail!

    std::string s;
    while (getline(file, s))
    {
        double value;
        StrToDouble(s.c_str(), value);
        data.push_back(value);
    }

    file.close();

    return E_NO_ERROR;
}

/**
 * @brief 读取二进制文件
 * @param file_name: 文件名
 * @param data: 存储的数据， 一般元数据类型为 uint8_t
 * @return < 0, if 读取失败
 */
template <typename T>
int ReadBinaryFile(const std::string& file_name, std::vector<T>& data)
{
    std::ifstream binary_file(file_name, std::ios::binary);
    if(!binary_file.is_open())
        return E_FILE_NOT_EXIST;

    binary_file.seekg (0, std::ios::end);
    int byte_num = binary_file.tellg();
    binary_file.seekg (0, std::ios::beg);

    data.resize(byte_num);
    binary_file.read((char*)&data[0], sizeof(T) * byte_num);

    return E_NO_ERROR;
}

#endif // IOSTREAM_OPERATION_H
